package com.tunnel.common.tunnel;

import java.util.HashMap;
import java.util.Map;

import com.tunnel.common.constant.Constant;
import com.tunnel.common.task.BaseTask;
import com.tunnel.common.util.LogUtil;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;

public class TunnelDataSenderManager {
	//ping消息
	public static final byte PING_MSG = 1;
    //注册信息
	public static final byte REGISTER_MSG = 2;
	
	//##############HTTP####################
    //HTTP数据
    public static final byte HTTP_DATA_MSG = 3;
    //停止tunnel-client的http task，这会导致tunnel中的http数据尽快停止传输，并触发浏览器与tunnel-server的连接断开
    public static final byte STOP_HTTP_TASK_MSG = 4;
    
    //#############TCP/IP##################
    //TCP建立连接信号
    public static final byte TCP_CONNECT_MSG = 5;
    //TCP数据
    public static final byte TCP_DATA_MSG = 6;
    //停止tuunel-client与app的TCP连接 
    public static final byte TCP_CLOSE_MSG = 7;
    
    //#############UDP#####################
    //UDP数据
    public static final byte UDP_DATA_MSG = 8;

    public static final Map<ChannelHandlerContext,TunnelDataQueue> CTX_QUEUE_MAP = new HashMap<>();
    
    public static void commitData(byte flag,ChannelHandlerContext ctx,byte[] ...data){
    	commitData(null, flag, ctx, data);
    }
    
    public static void commitData(BaseTask task,byte flag,ChannelHandlerContext ctx,byte[] ...datas){
//    	LogUtil.d("commitData in");
    	TunnelDataQueue tunnelDataQueue = null;
    	synchronized (CTX_QUEUE_MAP) {
//    		LogUtil.d("commitData hold");
    		if(CTX_QUEUE_MAP.containsKey(ctx)){
    			tunnelDataQueue = CTX_QUEUE_MAP.get(ctx);
        	}
		}
    	if(tunnelDataQueue != null){
    		TunnelData tunnelData = new TunnelData(task,flag, datas);
    		boolean addSuccess = tunnelDataQueue.addDataToQueue(tunnelData);
    		while(!addSuccess){
    			try {
    				Thread.sleep(100);
    			} catch (Exception e) {}
    			addSuccess = tunnelDataQueue.addDataToQueue(tunnelData);
    		}
    	}
//		LogUtil.d("commitData out");
    }
    
    public static void addQueue(TunnelDataQueue queue){
//    	LogUtil.d("addQueue in");
    	synchronized (CTX_QUEUE_MAP) {
//        	LogUtil.d("addQueue hold");
    		if(!CTX_QUEUE_MAP.containsKey(queue.getCtx())){
    			CTX_QUEUE_MAP.put(queue.getCtx(), queue);
    		}
    	}
//    	LogUtil.d("addQueue out");
    }
    
    public static void closeQueue(ChannelHandlerContext ctx){
//    	LogUtil.d("closeQueue in");
    	TunnelDataQueue tunnelDataQueue = null;
    	synchronized (CTX_QUEUE_MAP) {
//        	LogUtil.d("closeQueue hold");
    		if(CTX_QUEUE_MAP.containsKey(ctx)){
    			tunnelDataQueue = CTX_QUEUE_MAP.get(ctx);
    		}
    	}
    	if(tunnelDataQueue != null){
			tunnelDataQueue.stop();
    	}
//    	LogUtil.d("closeQueue out");
    }
    
    public static void doSendData(ChannelHandlerContext ctx,TunnelData tunnelData){
    	try {
    		do{
	    		if(ctx.isRemoved() || !ctx.channel().isActive()){
	    			closeQueue(ctx);
	    			break;
	    		}
	    		
	    		if(tunnelData.getTask() != null && tunnelData.getTask().isStop()){
	    			//如果数据包是指定了来源任务的，并且来源任务已经停了，那么数据包也停止发送
	    			//及时停止
	    			break;
	    		}
	    		
	    		int dataLen = 0;
            	for(byte[] dataOne:tunnelData.getDatas()){
            		if(dataOne != null){
            			dataLen = dataLen+dataOne.length;
            		}
            	}
            	/*if(dataLen > 0){
                	LogUtil.d("tunnel send:falg["+tunnelData.getFlag()+"] size["+dataLen+"b]");
            	}*/
            	/*if(tunnelData.getFlag() == PING_MSG){
            		break;
//            		LogUtil.d("beat heart");
            	}*/
            	
            	
            	ByteBuf buf = Unpooled.buffer(1+dataLen+Constant.TUNNEL_DATA_END_FLAG_BYTES.length);  
                buf.writeByte(tunnelData.getFlag());//发送一个数据标识位
                for(byte[] dataOne:tunnelData.getDatas()){
                	if(dataOne != null){
//	                	LogUtil.d("tunnel send:"+new String(dataOne));
                		buf.writeBytes(dataOne);//发送数据
                	}
                }
                buf.writeBytes(Constant.TUNNEL_DATA_END_FLAG_BYTES);//发送结束标识
                
                //netty channel里发送数据是都堆在堆内存里的，空间限制，因此如果写的太快，能回oom
                int waiteMs = 1;
                while(!ctx.channel().isWritable()){
                	try {
                		Thread.sleep(waiteMs);
					} catch (Exception e) {}
                	waiteMs = waiteMs+1;
                }
                
                ctx.writeAndFlush(buf);
    		}while(false);
		} catch (Exception e) {
			LogUtil.e(e);
		}
    }
    
}
